﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Engine;
using Engine.Physics;
using Engine.Graphics;

namespace Playtime
{
    public class Player : Component
    {
        public const float SPEED = 5000;
        Vector2 prevPos = Vector2.Zero;
        Vector2 vel = Vector2.Zero;
        Vector2 gravity = new Vector2(0, 1900);
        float friction = 15f;
        float maxVel = 300;
        Vector2 groundDir;

        JointAnimationContainer animationContainer;
        JointStructure structure;

        bool grounded;

        public Player()
        {

        }

        public override void Awake()
        {
            base.Awake();
            animationContainer = gameObject.GetComponent<JointAnimationContainer>();
            structure = gameObject.GetComponent<JointStructure>();
        }

        public override void OnCollision(CollisionInfo _collInfo)
        {
            OBB collOBB = _collInfo.collider2.OrientedBoundingBox;

            Vector2 diff = Position + new Vector2(0, gameObject.collider.AABB.Height / 2) - (_collInfo.collider2.Position - collOBB.LocalAxis[1] * 15);
            Vector2 prevDiff = prevPos + new Vector2(0, gameObject.collider.AABB.Height / 2) - (_collInfo.collider2.Position - collOBB.LocalAxis[1] * 15);

            float dot = Vector2.Dot(collOBB.LocalAxis[1], diff);
            float prevDot = Vector2.Dot(collOBB.LocalAxis[1], prevDiff);

            Vector2 projectionX = collOBB.LocalAxis[0] * Vector2.Dot(collOBB.LocalAxis[0], diff);
            Vector2 projectionY = collOBB.LocalAxis[1] * Vector2.Dot(collOBB.LocalAxis[1], diff);
            Vector2 prevProjectionY = collOBB.LocalAxis[1] * Vector2.Dot(collOBB.LocalAxis[1], prevDiff);

            Vector2 projectedVelocityY = collOBB.LocalAxis[1] * Vector2.Dot(collOBB.LocalAxis[1], vel);

            if (projectedVelocityY.Y > 0 && projectionX.Length() - gameObject.collider.Width / 2 < _collInfo.collider2.Width / 2 && projectionY.Y > 0 && prevProjectionY.Y < 3)
            {
                Position -= (diff - projectionX);
                
                float collRot = Math.Abs(_collInfo.collider2.gameObject.transform.Rotation) % MathHelper.TwoPi;
                if (collRot > 0.785398163f && collRot < MathHelper.TwoPi - 0.785398163f)
                    vel.X = 0;
                else
                {
                    vel.Y = 0;
                    grounded = true;
                    groundDir = collOBB.LocalAxis[0];
                }
                
            }            
        }

        public override void Update(GameTime _gameTime)
        {
            if (InputHandler.KeyPressed(Keys.E))
                Position = GraphicsEngine.Instance.Resolution / 2;
            prevPos = Position;
            Vector2 pos = Position;
            Vector2 acc = Vector2.Zero;

            acc = gravity;

            float actualSpeed = SPEED;
            if (grounded)
            {
                if (InputHandler.KeyPressed(Keys.Space))
                {
                    grounded = false;
                    groundDir = Vector2.UnitX;
                    vel = new Vector2(vel.X, -700);
                }
            }
            else
            {
                actualSpeed *= 0.3f;
                if (InputHandler.KeyReleased(Keys.Space) && vel.Y < 0)
                    vel = new Vector2(vel.X, vel.Y / 1.4f);
            }


            if (InputHandler.KeyDown(Keys.A))
                acc -= actualSpeed * groundDir;
            if (InputHandler.KeyDown(Keys.D))
                acc += actualSpeed * groundDir;


            MathUtils.IntegrateVerlet(ref acc, ref vel, ref pos, (float)_gameTime.ElapsedGameTime.TotalSeconds);

            if (Math.Abs(vel.X) > maxVel)
            {
                if (vel.X > 0)
                    vel.X = maxVel;
                else
                    vel.X = -maxVel;
            }

            if (grounded)
            {
                vel -= new Vector2(vel.X * friction * (float)_gameTime.ElapsedGameTime.TotalSeconds, 0);
            }

            groundDir = Vector2.UnitX;

            Console.WriteLine(grounded);

            if (Position != pos && (Position - pos).Length() > 16f * (float)_gameTime.ElapsedGameTime.TotalSeconds)
            {
                if (Position.X <= pos.X)
                    structure.IsMirrored = true;
                else
                    structure.IsMirrored = false;

                animationContainer.Play("Run");
                Position = pos;
            }
            else
                animationContainer.Pause();


            if (!grounded)
                Position = pos;
        }
    }
}
